package me.zhengjie.utils;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.Collator;
import java.util.*;

/**
 * <desc>
 *      md5加密帮助类
 * </desc>
 *
 * @createDate
 */
public class Md5EncryptUtil {
    private static final Logger LOG = LoggerFactory.getLogger(Md5EncryptUtil.class);

    /**
     * <desc>
     *      MD5加密
     * </desc>
     *
     * @param str
     * @return MD5加密字符串
     * @author Jiaqi.X
     * @createDate 2017/09/15
     */
    public static String md5(String str) {
        return DigestUtils.md5Hex(str);
    }

    /**
     *<desc>
     *      app签名MD5值
     *      规则：通过实体类属性名ascii码值顺序value+MD5加密串，返回MD5加密值
     *</desc>
     *
     * @param MOBILE_MD5_KEY md5加密串
     * @param model dto传输模型
     * @return
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @author Jiaqi.X
     * @createDate 2017/09/15
     */
    public static <T> boolean checkAppSign(T model ,String appSign,String MOBILE_MD5_KEY) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        Class clazz = model.getClass();
        Field[] fields = clazz.getDeclaredFields();
        Arrays.sort(fields, new Comparator<Field>() {
            public int compare(Field o1, Field o2) {
                return Collator.getInstance(Locale.ENGLISH).compare(o1.getName().toLowerCase(), o2.getName().toLowerCase());
            }
        });
        StringBuilder sb = new StringBuilder();
        for(Field field : fields) {
            if(!field.getName().equals("token")&&!field.getName().equals("createTime")&&!field.getName().equals("language")) {
                sb.append(field.getName());
            }
        }
        //获取get属性方法名
        Object language;
        Method lm ;
        try {
            lm = model.getClass().getMethod("getLanguage");
        }catch (Exception e){
            lm=null;
        }
        if(lm!=null) {
            language = lm.invoke(model);
            if (language != null) {
                sb.append(language);
            }
        }
        //获取get属性方法名
        Object value;
        Method m = model.getClass().getMethod("getCreateTime");
        value = m.invoke(model);
        sb.append(value);
        sb.append(MOBILE_MD5_KEY);
//        System.out.println("排序后Key结果----"+sb.toString());
        return md5(sb.toString()).equals(appSign);
    }

    /**
     *<desc>
     *      app签名MD5值，使用参数value，而不是key
     *      规则：通过实体类属性名ascii码值顺序value+MD5加密串，返回MD5加密值
     *</desc>
     *
     * @param MOBILE_MD5_KEY md5加密串
     * @param model dto传输模型
     * @return
     * @throws IllegalAccessException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @author wangzhi
     * @createDate 2019/09/19
     */
    public static <T> boolean checkAppSignValue(T model ,String appSign,String MOBILE_MD5_KEY) throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        Class clazz = model.getClass();
        Field[] fields = clazz.getDeclaredFields();
        Arrays.sort(fields, new Comparator<Field>() {
            public int compare(Field o1, Field o2) {
                return Collator.getInstance(Locale.ENGLISH).compare(o1.getName().toLowerCase(), o2.getName().toLowerCase());
            }
        });
        StringBuilder sb = new StringBuilder();
        for(Field field : fields) {
            if(!field.getName().equals("token")&&!field.getName().equals("createTime")&&!field.getName().equals("language")) {
                field.setAccessible(true);
                sb.append(field.get(model) == null ? "" : field.get(model));
            }
        }
        //获取get属性方法名
        Object language;
        Method lm ;
        try {
            lm = model.getClass().getMethod("getLanguage");
        }catch (Exception e){
            lm=null;
        }
        if(lm!=null) {
            language = lm.invoke(model);
            if (language != null) {
                sb.append(language);
            }
        }
        //获取get属性方法名
        Object value;
        Method m = model.getClass().getMethod("getCreateTime");
        value = m.invoke(model);
        sb.append(value);
        sb.append(MOBILE_MD5_KEY);
        System.out.println("排序后Key结果----"+md5(sb.toString()));
        return md5(sb.toString()).equals(appSign);
    }

    /**
     * <desc>
     *     APP端校验参数及签名串MD5值
     * </desc>
     * @param params
     * @param appSign
     * @param MOBILE_MD5_KEY
     * @return
     */
    public static boolean checkAppSign(Map<String,String> params,String appSign,String MOBILE_MD5_KEY){
        if (params == null|| StringUtils.isBlank(appSign)){
            return  false;
        }
        StringBuffer stringA = new StringBuffer();
        List<Map.Entry<String,String>> mappingList = new ArrayList<Map.Entry<String,String>>(params.entrySet());
        Collections.sort(mappingList, new Comparator<Map.Entry<String, String>>() {
            @Override
            public int compare(Map.Entry<String, String> mappingF, Map.Entry<String, String> mappingS) {
                return mappingF.getKey().toLowerCase().compareTo(mappingS.getKey().toLowerCase());
            }
        });
        String createTimeValue = "",languageValue = "";
        for (Map.Entry<String,String> mapping : mappingList){
            if ("createTime".equals(mapping.getKey())){
                createTimeValue = mapping.getValue();
            }else if ("language".equals(mapping.getKey())){
                languageValue = mapping.getValue();
            }else{
                stringA.append(mapping.getKey());
            }
        }
        stringA.append(languageValue);
        stringA.append(createTimeValue);

        String result = stringA.append(MOBILE_MD5_KEY).toString();
//        System.out.println("result:"+result);
        result = md5(result);
//        System.out.println("appSign:"+appSign);
        return result.equals(appSign);
    }

    /**
     * <desc>
     *     APP端校验参数及签名串MD5值
     * </desc>
     * @param params
     * @param appSign
     * @param MOBILE_MD5_KEY
     * @return
     */
    public static boolean checkAppSignValue(Map<String,String> params,String appSign,String MOBILE_MD5_KEY){
        if (params == null|| StringUtils.isBlank(appSign)){
            return  false;
        }
        StringBuffer stringA = new StringBuffer();
        List<Map.Entry<String,String>> mappingList = new ArrayList<Map.Entry<String,String>>(params.entrySet());
        Collections.sort(mappingList, new Comparator<Map.Entry<String, String>>() {
            @Override
            public int compare(Map.Entry<String, String> mappingF, Map.Entry<String, String> mappingS) {
                return mappingF.getKey().toLowerCase().compareTo(mappingS.getKey().toLowerCase());
            }
        });
        String createTimeValue = "",languageValue = "";
        for (Map.Entry<String,String> mapping : mappingList){
            if ("createTime".equals(mapping.getKey())){
                createTimeValue = mapping.getValue();
            }else if ("language".equals(mapping.getKey())){
                languageValue = mapping.getValue();
            }else{
                stringA.append(mapping.getValue() == null ? "" : mapping.getValue());
            }
        }
        stringA.append(languageValue);
        stringA.append(createTimeValue);

        String result = stringA.append(MOBILE_MD5_KEY).toString();
//        System.out.println("result:"+result);
        result = md5(result);

//        System.out.println("appSign:"+appSign);
        return result.equals(appSign);

        // 兼容小程序未更新验签
//        return result.equals(appSign) || checkAppSign(params, appSign, MOBILE_MD5_KEY);
    }

    /**
     * <desc>
     *      APP端校验参数及签名串MD5值
     * </desc>
     *
     * @param params 验签参数集
     * @param appSign app端验签结果
     * @param MOBILE_MD5_KEY 验签key
     * @param mark 预留参数
     * @return
     */
    public static boolean checkAppSignValue(Map<String, Object> params, String appSign, String MOBILE_MD5_KEY, String mark) {
        if (params == null || StringUtils.isBlank(appSign)) {
            return false;
        }
        StringBuffer stringA = new StringBuffer();
        List<Map.Entry<String, Object>> mappingList = new ArrayList<Map.Entry<String, Object>>(params.entrySet());
        Collections.sort(mappingList, new Comparator<Map.Entry<String, Object>>() {
            @Override
            public int compare(Map.Entry<String, Object> mappingF, Map.Entry<String, Object> mappingS) {
                return mappingF.getKey().toLowerCase().compareTo(mappingS.getKey().toLowerCase());
            }
        });
        String createTimeValue = "", languageValue = "";
        for (Map.Entry<String, Object> mapping : mappingList) {
            if ("createTime".equals(mapping.getKey())) {
                createTimeValue = mapping.getValue() == null ? "" : mapping.getValue().toString();
            } else if ("language".equals(mapping.getKey())) {
                languageValue = mapping.getValue() == null ? "" : mapping.getValue().toString();
            } else {
                stringA.append(mapping.getValue() == null ? "" : mapping.getValue().toString());
            }
        }
        stringA.append(languageValue);
        stringA.append(createTimeValue);

        String result = stringA.append(MOBILE_MD5_KEY).toString();
        result = md5(result);
//        System.out.println("result:" + result);
//        System.out.println("appSign:" + appSign);
        return result.equals(appSign);
    }

    /**
     * <desc>
     *     云设备计算MD5
     * </desc>
     * @param params
     * @param appsecret
     * @return
     */
    public static String cloudDeviceMd5(Map<String,Object> params,String createTime, String appsecret) {
        if (params == null || StringUtils.isBlank(appsecret)) {
            return null;
        }
        StringBuffer stringA = new StringBuffer();
        List<Map.Entry<String, Object>> mappingList = new ArrayList<Map.Entry<String, Object>>(params.entrySet());
        Collections.sort(mappingList, new Comparator<Map.Entry<String, Object>>() {
            @Override
            public int compare(Map.Entry<String, Object> mappingF, Map.Entry<String, Object> mappingS) {
                return mappingF.getKey().toLowerCase().compareTo(mappingS.getKey().toLowerCase());
            }
        });
        for (Map.Entry<String, Object> mapping : mappingList) {
            stringA.append(mapping.getValue() == null ? "" : mapping.getValue());
        }
        stringA.append(createTime);
        return md5(stringA.append(appsecret).toString());
    }

    /**
     * <desc>
     *     云电梯SDK校验参数及签名串MD5值
     * </desc>
     * @param params
     * @param appSign
     * @param MOBILE_MD5_KEY
     * @return
     */
    public static boolean checkCloudTalkSDKSign(Map<String,Object> params,String appSign,String MOBILE_MD5_KEY){
        if (params == null|| StringUtils.isBlank(appSign)){
            return  false;
        }
        StringBuffer stringA = new StringBuffer();
        List<Map.Entry<String,Object>> mappingList = new ArrayList<Map.Entry<String,Object>>(params.entrySet());
        Collections.sort(mappingList, new Comparator<Map.Entry<String, Object>>() {
            @Override
            public int compare(Map.Entry<String, Object> mappingF, Map.Entry<String, Object> mappingS) {
                return mappingF.getKey().toLowerCase().compareTo(mappingS.getKey().toLowerCase());
            }
        });
        String createTimeValue = "";
        for (Map.Entry<String,Object> mapping : mappingList){
            if ("createTime".equals(mapping.getKey())){
                createTimeValue = mapping.getValue().toString();
            }else{
                stringA.append(mapping.getValue() == null ? "" : mapping.getValue().toString());
            }
        }
        stringA.append(createTimeValue);
        String result = stringA.append(MOBILE_MD5_KEY).toString();
        result = md5(result);
        return result.equals(appSign);
    }

    /**
     * <desc>
     *     sdkV2版本验签方法
     * </desc>
     * @param params
     * @param appSign
     * @param appsecret
     * @return
     */
    public static boolean checkSDKV2Sign(Map<String,Object> params,String appSign,String requestId,String timestamp,String appsecret){
        if (params == null|| StringUtils.isBlank(appSign)){
            return  false;
        }
        StringBuffer stringA = new StringBuffer();
        List<Map.Entry<String,Object>> mappingList = new ArrayList<Map.Entry<String,Object>>(params.entrySet());
        Collections.sort(mappingList, new Comparator<Map.Entry<String, Object>>() {
            @Override
            public int compare(Map.Entry<String, Object> mappingF, Map.Entry<String, Object> mappingS) {
                return mappingF.getKey().toLowerCase().compareTo(mappingS.getKey().toLowerCase());
            }
        });
        for (Map.Entry<String,Object> mapping : mappingList) {
            stringA.append(mapping.getValue() == null ? "" : mapping.getValue().toString());
        }
        stringA.append(requestId);
        stringA.append(timestamp);
        stringA.append(appsecret);
        String result = md5(stringA.toString());
        return result.equals(appSign);
    }

    /**
     * <desc>
     *     sdkv2计算MD5
     * </desc>
     * @param params
     * @param appsecret
     * @return
     */
    public static String getSDKV2Md5(Map<String,Object> params,String requestId,String timestamp,String appsecret) {
        if (params == null || StringUtils.isBlank(appsecret)) {
            return null;
        }
        StringBuffer stringA = new StringBuffer();
        List<Map.Entry<String, Object>> mappingList = new ArrayList<Map.Entry<String, Object>>(params.entrySet());
        Collections.sort(mappingList, new Comparator<Map.Entry<String, Object>>() {
            @Override
            public int compare(Map.Entry<String, Object> mappingF, Map.Entry<String, Object> mappingS) {
                return mappingF.getKey().toLowerCase().compareTo(mappingS.getKey().toLowerCase());
            }
        });
        for (Map.Entry<String, Object> mapping : mappingList) {
            stringA.append(mapping.getValue() == null ? "" : mapping.getValue());
        }
        stringA.append(requestId);
        stringA.append(timestamp);
        stringA.append(appsecret);
        return md5(stringA.toString());
    }

    /**
     * <desc>
     * sdkV3版本验签方法
     * </desc>
     *
     * @param params
     * @param appSign
     * @param appsecret
     * @return
     */
    public static boolean checkSDKV3Sign(Map<String, Object> params, String appsecret, String appSign, String... values) {
        String result = getSDKV3Sign(params, appsecret, values);
        if (StringUtils.isEmpty(result)) {
            return false;
        }
        return result.equals(appSign);
    }


    /**
     * <desc>
     * sdkv3计算sign
     * </desc>
     *
     * @param params
     * @param appsecret
     * @return
     */
    public static String getSDKV3Sign(Map<String, Object> params, String appsecret, String... values) {
        if (params == null || StringUtils.isBlank(appsecret)) {
            return null;
        }
        StringBuffer stringA = new StringBuffer();
        List<Map.Entry<String, Object>> mappingList = new ArrayList<Map.Entry<String, Object>>(params.entrySet());
        Collections.sort(mappingList, new Comparator<Map.Entry<String, Object>>() {
            @Override
            public int compare(Map.Entry<String, Object> mappingF, Map.Entry<String, Object> mappingS) {
                return mappingF.getKey().toLowerCase().compareTo(mappingS.getKey().toLowerCase());
            }
        });
        for (Map.Entry<String, Object> mapping : mappingList) {
            stringA.append(mapping.getValue() == null ? "" : mapping.getValue());
        }
        for (String value : values) {
            stringA.append(value);
        }
        stringA.append(appsecret);
        return md5(stringA.toString());
    }

    /**
     * <desc>
     *      将userId转化为8位的HEX(MD5加密后截取奇数位)
     * <desc/>
     * @param  source 待加密的MD5数据
     * @return 生成的HEX
     * @author Biao.R
     * @createDate 2018/1/24
     */
    public static String offLineUserIdHandler(String source) {
        if (StringUtils.isEmpty(source)) {
            return "";
        }
        if (source.length() <= 8) {
            return source;
        }
        StringBuffer sb = new StringBuffer(8);
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] array = md.digest(source.getBytes("utf-8"));
            //遍历获取4到12位，得到MD5的16位数，并取其中的奇数位
            for (int i = 4; i < 12; i++) {
                String substring = Integer.toHexString((array[i] & 0xFF) | 0x100).toUpperCase().substring(1, 3);
                sb.append(substring.charAt(0));
            }
        } catch (Exception e) {
            return null;
        }
        return sb.toString();
    }
    /**
     * <desc>
     *      md5加密
     * </desc>
     * @param psd
     * @param salt
     * @return
     */
    public static String enWithSalt(String psd, String salt) {
        try {
            StringBuffer stingBuffer = new StringBuffer();
            // 1.指定加密算法
            MessageDigest digest = MessageDigest.getInstance("MD5");
            // 2.将需要加密的字符串转化成byte类型的数据，然后进行哈希过程
            byte[] bs = digest.digest((psd + salt).getBytes());
            // 3.遍历bs,让其生成32位字符串，固定写法
            // 4.拼接字符串
            for (byte b : bs) {
                int i = b & 0xff;
                String hexString = Integer.toHexString(i);
                if (hexString.length() < 2) {
                    hexString = "0" + hexString;
                }
                stingBuffer.append(hexString);
            }
            return stingBuffer.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * <desc>
     *      微信支付签名算法
     * </desc>
     *
     * @param map 要参与签名的数据对象
     * @param key 参与加密的key
     * @return 签名
     * @throws IllegalAccessException
     * @author Jiaqi.X
     * @createDate 2018/04/18
     */
    public static String getWechatSign(Map<Object,Object> map,String key){
        ArrayList<String> list = new ArrayList<String>();
        for(Map.Entry<Object,Object> entry:map.entrySet()){
            if(entry.getValue()!=""){
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + key;
        LOG.info("Sign Before MD5:" + result);
        result = md5(result).toUpperCase();
        LOG.info("Sign Result:" + result);
        return result;
    }

}
